perm filename REVSND[REV,MUS] blob
sn#265524 filedate 1977-05-24 generic text, type C, neo UTF8
COMMENT ⊗ VALID 00005 PAGES
C REC PAGE DESCRIPTION
C00001 00001
C00002 00002 How to reverberate a sound file:
C00008 00003 How to calculate Norms and Permute
C00010 00004 How to create TREE of states:
C00014 00005 How to fetch units from REV file:
C00016 ENDMK
C⊗;
How to reverberate a sound file:
In what follows, all tree traversals are prefix walks, i.e. the order is:
(process this node,
traverse the OUTPUT subtree of this node,
traverse all the ABURO subtrees of this node).
A node in the tree is of the form:
[OUTPUT: pointer to first output node,
ABURO: pointer to younger sibs of this node,
fields for the parameters to APR for this unit]
Each node in the tree contains a description of a single unit reverberator
sufficient for calling the all-pass reverberator simulator routine APR. The
best version of that routine is to be found on my area [REV,KS] named APRL.FAI,
where the L means lattice form. The source includes a sample SAIL declaration.
Various programming notations used below are explained afterward. For simplicity,
I will use, e.g. ABURO[node], to mean TREE:ABURO[node] if no ambiguity results.
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-
Create TREE, counting POLY ← # of branches.
Allocate array of output buffers REAL Bufs[1:POLY,1:BUFSIZE];
Allocate parallel array of normalizing factors REAL Norms[1:POLY];
Allocate parallel array of PNT(TREE) Backup[0:POLY];
Allocate parallel array of indices INT Permute[0:POLY];
For efficiency, these can be made static (OWN) by assuming that
POLY never exceeds 4 (quad reverberator). POLY must still
be determined however.
Calculate Norms and Permute; {Explained below.}
Init input file, output file;
LOOP: this_one ← 1; next_one ← 2; Backup[*] ← λ; node ← first node in tree;
Read input into Bufs[Permute[this_one],*];
WHILE ¬eof: {reverberate this buffer into POLY channel buffers}
LOOP:WHILE node ≠ λ: {go thru TREE}
IF
ABURO[node] = λ
THEN
gets ← this_one
ELSE
gets ← next_one; next_one ← next_one+1;
Backup[this_one] ← ABURO[node];
APR(Bufs[Permute[this_one],1],Bufs[Permute[gets],1],BUFSIZE,
..unit parameters..);
this_one ← gets;
Backup[this_one] ← OUTPUT[node];
LOOP:WHILE Backup[this_one] = λ ∧ this_one > 0:
this_one ← this_one-1;
REPEAT;
node ← Backup[this_one];
REPEAT;
Multiply each buffer by the appropriate normalizing factor:
∀(1 ≤ i ≤ POLY,1 ≤ j ≤ BUFSIZE) Bufs[i,j] ← Bufs[i,j]*Norms[i];
{now multiplex buffers into output. This is clever.}
{treat Bufs as 1-dimensional array, indexed from 0}
i ← 0;
LOOP: {get the next output sample}
next output sample ← Bufs[i];
i ← i+BUFSIZE;
WHILE i ≠ (POLY+1)*BUFSIZE:
IF
i ≥ POLY*BUFSIZE
THEN
i ← i+1-POLY*BUFSIZE;
REPEAT;
Write output;
REPEAT;
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-
The looping syntax used is from Knuth's "Structured programming with goto's",
and is interpretted as follows:
LOOP: S1; WHILE B: S2; REPEAT;
means execute S1, then test condition B; if B is FALSE, then exit the loop, else
execute S2 and repeat the loop starting at S1 again.
Where I refer to array subscripts with the notation A[*] or B[1,*] I mean all
values of that particular index; so that A[*]←λ means
FOR i←lower_bound_of_A STEP 1 UNTIL upper_bound_of_A DO A[i]←λ;
I use the abbreviation λ to mean NULL_RECORD.
Curly brackets {} surround comments.
Specific details of I/O are left as an exercise for the reader.
How to calculate Norms and Permute
PROCEDURE normute(
RECORD_POINTER(TREE) node;
REAL gain;
REFERENCE REAL ARRAY Norms;
REFERENCE INTEGER ARRAY Permute;
REFERENCE INTEGER this, next, out#);
BEGIN "normute"
REAL lgain;
DO BEGIN
lgain ← gain * GAIN[node];
IF
ABURO[node] ≠ λ
THEN BEGIN
this ← next;
next ← next+1;
END;
IF
OUTPUT[node] = λ
THEN BEGIN
Permute[this] ← out#;
Norms[out#] ← 1.0/lgain;
out# ← out#+1;
DO
this ← this-1
UNTIL
Permute[this] = 0;
END
ELSE
normute(OUTPUT[node],lgain,Norms,Permute,this,next,out#);
node ← ABURO[node];
END
UNTIL
node = λ;
END "normute"
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-
Call this routine with:
normute(root of tree,1.0,Norms,Permute,tmp1,tmp2,tmp3);
How to create TREE of states:
1) write an input routine
"BOOL PROC fetch_unit(INT channel; REF INT pos, delay; REF REAL gain)"
which returns TRUE iff it could fetch another unit, and FALSE with pos=INFINITY
otherwise. Pos = -1 => Append, 0 => Branch, >0 => pop(Pos times)&Branch.
It can tell when there are no more units by encountering a blank line.
2) write a routine
"PNT(TREE) PROC grow(INT delay; REAL gain)"
which constructs a node of form:
TREE[OUTPUT: pointer to first output node,
ABURO: pointer to first younger sib node,
the following are parameters to the APR routine ...
MEM[1:delay]: array for delay memory,
delay: length of delay memory,
pos: pointer into MEM maintained by APR,
gain: attenuation factor for this unit]
3) use the recursive sub-tree builder listed below
"PROC acorn(INT channel; PNT(TREE) node;
REF INT poly, pos, delay; REF REAL gain)"
which uses (1) and (2) to build the TREE.
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-
PROCEDURE acorn(
INTEGER channel;
RECORD_POINTER(TREE) node;
REFERENCE INTEGER poly, pos, delay;
REFERENCE REAL gain);
BEGIN "acorn"
WHILE
fetch_unit(channel,delay,pos,gain)
DO BEGIN
IF
pos < 0
THEN BEGIN
OUTPUT[node] ← grow(delay,gain);
acorn(channel,OUTPUT[node],poly,pos,delay,gain);
END;
IF
pos > 0
THEN BEGIN
pos ← pos-1;
RETURN;
END;
ABURO[node] ← grow(delay,gain);
node ← ABURO[node];
poly ← poly+1;
END;
pos ← INFINITY;
END "acorn";
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-
Call acorn as follows:
ignore commands from REV file thru first blank line.
next line is Clock rate; input sound file must use same rate.
next line may be ignored.
next line is the first line to be read by fetch_unit.
call acorn(channel,rev←NEW_RECORD(TREE),poly←1,tmp,tmp,tmp).
then throw away first node in tree.
How to fetch units from REV file:
Units will be described by two lines like this:
#SAMPLES 9999
GAIN .999 DELAY
The fetch_unit routine should simply return these two values in the corresponding
reference parameters delay(i.e. #samples delay) and gain.
These two lines will be immediately followed (no intervening blank lines) by
some number of lines describing where to put this unit in the tree constructed
so far. There are two possibilities:
APPEND
or
{←}*
BRANCH
The second case means there will be 0 or more lines containing the single
command "←", followed by a line with the BRANCH command. In this case, pos
should be the number of "←" commands seen. In the APPEND case, pos should
be returned as -1.
If a blank line is seen, then there are no further units in the tree, so the
boolean value of the routine should be FALSE. In this case, pos should be
returned with the value INFINITY. If a unit is found (the usual case), then
the value of the routine should be TRUE to indicate success.